/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.commons.lang;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
 */
public final class Secrets {
    private Secrets() {
    }

    /**
     * 获取输入流中数据的MD5码
     *
     * @param is 输入流
     * @return 返回输入流中数据的MD5码
     */
    public static byte[] md5(InputStream is) throws IOException, NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance("MD5");
        byte[] buf = new byte[Streams.BUFFER_SIZE];
        int len;
        while ((len = is.read(buf)) != -1) {
            digest.update(buf, 0, len);
        }
        return digest.digest();
    }

    /**
     * 获取字符数组的MD5码
     *
     * @param bytes 字符数组
     * @return 返回给定字符数组的MD5码
     */
    public static byte[] md5(byte[] bytes) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(bytes);
            return md.digest();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取字符串的MD5码
     *
     * @param text 字符串
     * @return 返回给定字符串的MD5码
     */
    public static byte[] md5(String text) {
        return md5(text, StandardCharsets.UTF_8);
    }

    /**
     * 获取字符串的MD5码
     *
     * @param text 字符串
     * @param charsetName 字符串的编码方式
     * @return 返回字符串的MD5码
     */
    public static byte[] md5(String text, String charsetName) {
        try {
            return md5(text.getBytes(charsetName));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取字符串的MD5码
     *
     * @param text 字符串
     * @param charset 字符串的编码方式
     * @return 返回字符串的MD5码
     */
    public static byte[] md5(String text, Charset charset) {
        return md5(text.getBytes(charset));
    }

    /**
     * 获取字符数组的Base58编码MD5码
     *
     * @param bytes 字符数组
     * @return 返回字符数组的Base58编码MD5码
     */
    public static String md5Base58String(byte[] bytes) {
        return Base58.encode(md5(bytes));
    }
    
    /**
     * 获取字符串的Base58编码MD5码
     *
     * @param text 字符串
     * @return 返回给定字符串的Base58编码MD5码
     */
    public static String md5Base58String(String text) {
        return Base58.encode(md5(text));
    }
    
    /**
     * 获取字符串的Base58编码MD5码
     *
     * @param text 字符串
     * @param charsetName 字符串的编码方式
     * @return 返回字符串的Base58编码MD5码
     */
    public static String md5Base58String(String text, String charsetName) {
        return Base58.encode(md5(text, charsetName));
    }

    /**
     * 获取字符串的Base58编码MD5码
     *
     * @param text 字符串
     * @param charset 字符串的编码方式
     * @return 返回字符串的Base58编码MD5码
     */
    public static String md5Base58String(String text, Charset charset) {
        return Base58.encode(md5(text, charset));
    }

    /**
     * 获取字符数组的Hex编码MD5码
     *
     * @param bytes 字符数组
     * @return 返回字符数组的Hex编码MD5码（全为小写）
     */
    public static String md5HexString(byte[] bytes) {
        return Bytes.toHex(md5(bytes));
    }

    /**
     * 获取字符串的Hex编码MD5码
     *
     * @param text 字符串
     * @return 返回给定字符串的Hex编码MD5码（全为小写）
     */
    public static String md5HexString(String text) {
        return Bytes.toHex(md5(text));
    }

    /**
     * 获取字符串的Hex编码MD5码
     *
     * @param text 字符串
     * @param charsetName 字符串的编码方式
     * @return 返回字符串的Hex编码MD5码（全为小写）
     */
    public static String md5HexString(String text, String charsetName) {
        return Bytes.toHex(md5(text, charsetName));
    }

    /**
     * 获取字符串的Hex编码MD5码
     *
     * @param text 字符串
     * @param charset 字符串的编码方式
     * @return 返回字符串的Hex编码MD5码（全为小写）
     */
    public static String md5HexString(String text, Charset charset) {
        return Bytes.toHex(md5(text, charset));
    }

    /**
     * 获取字符数组的SHA1码
     *
     * @param bytes 字符数组
     * @return 返回字符数组的SHA1码
     */
    public static byte[] sha1(byte[] bytes) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA1");
            md.update(bytes);
            return md.digest();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取字符串的SHA1码
     *
     * @param text 字符串
     * @return 返回字符串的SHA1码
     */
    public static byte[] sha1(String text) {
        return sha1(text, StandardCharsets.UTF_8);
    }

    /**
     * 获取字符串的SHA1码
     *
     * @param text 字符串
     * @param charsetName 字符串的编码方式
     * @return 返回字符串的SHA1码
     */
    public static byte[] sha1(String text, String charsetName) {
        try {
            return sha1(text.getBytes(charsetName));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取字符串的SHA1码
     *
     * @param text 字符串
     * @param charset 字符串的编码方式
     * @return 返回字符串的SHA1码
     */
    public static byte[] sha1(String text, Charset charset) {
        return sha1(text.getBytes(charset));
    }

    /**
     * 获取字符数组的Hex格式SHA1码
     *
     * @param bytes 字符数组
     * @return 返回字符数组的Hex格式SHA1码
     */
    public static String sha1HexString(byte[] bytes) {
        return Bytes.toHex(sha1(bytes));
    }

    /**
     * 获取字符串的Hex格式SHA1码
     *
     * @param text 字符串
     * @return 返回字符串的Hex格式SHA1码
     */
    public static String sha1HexString(String text) {
        return Bytes.toHex(sha1(text));
    }

    /**
     * 获取字符串的Hex格式SHA1码
     *
     * @param text 字符串
     * @param charsetName 字符串的编码方式
     * @return 返回字符串的Hex格式SHA1码
     */
    public static String sha1HexString(String text, String charsetName) {
        return Bytes.toHex(sha1(text, charsetName));
    }

    /**
     * 获取字符串的Hex格式SHA1码
     *
     * @param text 字符串
     * @param charset 字符串的编码方式
     * @return 返回字符串的Hex格式SHA1码
     */
    public static String sha1HexString(String text, Charset charset) {
        return Bytes.toHex(sha1(text, charset));
    }
    
    /**
     * 获取字符数组的Base58编码SHA1码
     *
     * @param bytes 字符数组
     * @return 返回字符数组的Base58编码SHA1码
     */
    public static String sha1Base58String(byte[] bytes) {
        return Base58.encode(sha1(bytes));
    }
    
    /**
     * 获取字符串的Base58编码SHA1码
     *
     * @param text 字符串
     * @return 返回字符串的Base58编码SHA1码
     */
    public static String sha1Base58String(String text) {
        return Base58.encode(sha1(text));
    }
    
    /**
     * 获取字符串的Base58编码SHA1码
     *
     * @param text 字符串
     * @param charsetName 字符串的编码方式
     * @return 返回字符串的Base58编码SHA1码
     */
    public static String sha1Base58String(String text, String charsetName) {
        return Base58.encode(sha1(text, charsetName));
    }

    /**
     * 获取字符串的Base58编码SHA1码
     *
     * @param text 字符串
     * @param charset 字符串的编码方式
     * @return 返回字符串的Base58编码SHA1码
     */
    public static String sha1Base58String(String text, Charset charset) {
        return Base58.encode(sha1(text, charset));
    }

    /**
     * AES加密
     * @param content 要加密的内容
     * @param key 密钥
     * @return 返回加密后的内容
     */
    public static byte[] aesEncrypt(byte[] content, byte[] key) {
        try {
            byte[] iv = new byte[16];
            int len = Math.min(iv.length, key.length);
            System.arraycopy(key, 0, iv, 0, len);
            IvParameterSpec ivSpec = new IvParameterSpec(iv);

            SecretKeySpec keySpec = new SecretKeySpec(iv, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
            return cipher.doFinal(content);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * AES解密
     * @param content 要解密的内容
     * @param key 密钥
     * @return 返回解密后的内容
     */
    public static byte[] aesDecrypt(byte[] content, byte[] key) {
        try {
            byte[] iv = new byte[16];
            int len = Math.min(iv.length, key.length);
            System.arraycopy(key, 0, iv, 0, len);
            IvParameterSpec ivSpec = new IvParameterSpec(iv);

            SecretKeySpec keySpec = new SecretKeySpec(iv, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            return cipher.doFinal(content);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将value进行skip32加密
     * @param value 要加密码的值
     * @param key 加密密钥
     * @return 返回加密后的值
     */
    public static long skip32Encrypt(int value, String key) {
        return Skip32.encrypt(value, key);
    }

    /**
     * 将使用skip32加密的值解密
     * @param value 要解决的值
     * @param key 解密密钥
     * @return 返回解密后的值
     */
    public static int skip32Decrypt(long value, String key) {
        return Skip32.decrypt(value, key);
    }
    
}
